package com.falcon.parser;

import com.falcon.generator.vben.domain.ftl.ApiType;
import com.falcon.generator.vben.mapping.TsTypeMapping;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;

/**
 * @author zongf
 * @date 2024-03-17
 **/
@Slf4j
@Component
public class ApiTypeParser {

    public List<ApiType> parse(Class clz){
        Map<String, ApiType> typeMap = new HashMap<>();

        ApiType apiType = this.parseClass(clz);
        typeMap.put(apiType.getCanonicalName(), apiType);

        Queue<Class> queue = new LinkedList<>();
        apiType.getFields().stream()
                .map(ApiType.ApiField::getDependTypes)
                .flatMap(Collection::stream)
                .forEach(queue::add);
        // 添加父类
        if (Objects.nonNull(apiType.getParentClass())) {
            queue.add(apiType.getParentClass());
        }

        while (!queue.isEmpty()) {

            Class subClz = queue.poll();
            ApiType subType = this.parseClass(subClz);
            typeMap.put(subType.getCanonicalName(), subType);

            subType.getFields().stream()
                    .map(ApiType.ApiField::getDependTypes)
                    .flatMap(Collection::stream)
                    .filter(item -> !typeMap.containsKey(item.getCanonicalName()))
                    .filter(item -> !item.getCanonicalName().startsWith("java.lang"))
                    .forEach(queue::add);

            // 添加父类
            if (Objects.nonNull(subType.getParentClass())
                    && !subType.getParentClass().getCanonicalName().startsWith("java.lang")) {
                queue.add(subType.getParentClass());
            }
        }

        return new ArrayList<>(typeMap.values());
    }

    private ApiType parseClass(Class clz){
        Class superclass = (Class) clz.getGenericSuperclass();

        ApiType apiType = new ApiType();
        apiType.setName(clz.getSimpleName());
        apiType.setCanonicalName(clz.getCanonicalName());

        if (Objects.nonNull(superclass) && !"Object".equals(superclass.getSimpleName())) {
            apiType.setParentName(superclass.getSimpleName());
            apiType.setParentClass(superclass);
        }

        // 字段列表
        List<ApiType.ApiField> fieldList = new ArrayList<>();
        for (Field field : clz.getDeclaredFields()) {
            ApiType.ApiField apiField = new ApiType.ApiField();
            apiField.setName(field.getName());

            Class<?> fieldType = field.getType();

            String tsType = null;
            if (TsTypeMapping.isBasicType(fieldType.getSimpleName())) {
                // 基础类型
                tsType = TsTypeMapping.getTsType(fieldType.getSimpleName());
                apiField.setType(tsType);
                apiField.setTypeClass(fieldType);
            } else if (field.getType().isAssignableFrom(Set.class) || field.getType().isAssignableFrom(List.class)) {
                // 列表类型
                ParameterizedType paramType = (ParameterizedType) field.getGenericType();
                Type[] actualTypeArguments = paramType.getActualTypeArguments();
                if (actualTypeArguments.length > 0) {
                    apiField.setTypeClass((Class) actualTypeArguments[0]);

                    // 是否是基本类型
                    tsType = TsTypeMapping.getTsType(apiField.getTypeClass().getSimpleName());

                    if (Objects.isNull(tsType)) {
                        tsType = StringUtils.substringAfterLast(actualTypeArguments[0].getTypeName(), ".");
                        apiField.getDependTypes().add(apiField.getTypeClass());
                    }
                    apiField.setType(tsType + "[]");
                }
            } else {
                // 普通 java 对象类型
                apiField.setType(fieldType.getSimpleName());
                apiField.setTypeClass(fieldType);
                apiField.getDependTypes().add(fieldType);
            }
            fieldList.add(apiField);
        }
        apiType.setFields(fieldList);
        return apiType;
    }
}

